/*
 * Wazuh Vulnerability scanner - Scan Orchestrator
 * Copyright (C) 2015, Wazuh Inc.
 * May 6, 2024.
 *
 * This program is free software; you can redistribute it
 * and/or modify it under the terms of the GNU General Public
 * License (version 2) as published by the FSF - Free Software
 * Foundation.
 */

#ifndef _CVE_SOLVED_ALERT_DETAILS_BUILDER_HPP
#define _CVE_SOLVED_ALERT_DETAILS_BUILDER_HPP

#include "chainOfResponsability.hpp"
#include "databaseFeedManager.hpp"
#include "descriptionsHelper.hpp"
#include "fieldAlertHelper.hpp"
#include "numericHelper.h"
#include "scanContext.hpp"

/**
 * @brief Class in charge of building the solved alert details.
 * @tparam TScanContext scan context type.
 */
template<typename TDatabaseFeedManager = DatabaseFeedManager, typename TScanContext = ScanContext>
class TCVESolvedAlertDetailsBuilder final : public AbstractHandler<std::shared_ptr<TScanContext>>
{
private:
    std::shared_ptr<TDatabaseFeedManager> m_databaseFeedManager;

public:
    // LCOV_EXCL_START
    /**
     * @brief Construct a new CVE Solved Alert Details Builder object
     *
     * @param databaseFeedManager Database feed manager instance.
     */
    explicit TCVESolvedAlertDetailsBuilder(std::shared_ptr<TDatabaseFeedManager>& databaseFeedManager)
        : m_databaseFeedManager(databaseFeedManager)
    {
    }

    /**
     * @brief Class destructor.
     *
     */
    ~TCVESolvedAlertDetailsBuilder() = default;
    // LCOV_EXCL_STOP

    /**
     * @brief Handles request and passes control to the next step of the chain.
     *
     * @param context Scan context.
     * @return std::shared_ptr<ScanContext> Abstract handler.
     */
    std::shared_ptr<TScanContext> handleRequest(std::shared_ptr<TScanContext> context) override
    {
        // We only generate alerts for real time events, aka dbsync deltas.
        if (context->messageType() == MessageType::Delta)
        {
            const auto descriptionSources =
                DescriptionsHelper::cvssAndDescriptionSources<GlobalData>(context->m_vulnerabilitySource);

            for (const auto& [cve, elementData] : context->m_elements)
            {
                if (elementData.empty())
                {
                    continue;
                }

                try
                {
                    DescriptionsHelper::vulnerabilityDescription<TDatabaseFeedManager>(
                        cve,
                        descriptionSources,
                        m_databaseFeedManager,
                        [&](const CveDescription& description)
                        {
                            const std::string cvssVersion {description.scoreVersion};
                            const std::string scoreVersion {"cvss" + cvssVersion.substr(0, 1)};
                            nlohmann::json resultData;

                            const auto generateTitle = [&context, &cve]()
                            {
                                std::string title {cve};
                                title.append(" affecting ");
                                title.append("one or many packages");
                                title.append(" was solved by ");
                                title.append(context->hotfixId());
                                return title;
                            };
                            resultData["vulnerability"]["title"] = generateTitle();

                            // This improves the description of the alert without creating another rule
                            resultData["vulnerability"]["package"]["name"] = "one or many packages";
                            resultData["vulnerability"]["status"] = "Solved";

                            resultData["vulnerability"]["cve"] = cve;
                            resultData["vulnerability"]["scanner"]["reference"] = WAZUH_CTI_CVES_URL + cve;
                            if (!cvssVersion.empty())
                            {
                                resultData["vulnerability"]["cvss"][scoreVersion]["base_score"] =
                                    Utils::floatToDoubleRound(description.scoreBase, 2);
                            }
                            resultData["vulnerability"]["enumeration"] = "CVE";
                            resultData["vulnerability"]["published"] = description.datePublished;
                            resultData["vulnerability"]["reference"] = description.reference;
                            resultData["vulnerability"]["severity"] = FieldAlertHelper::fillEmptyOrNegative(
                                Utils::toSentenceCase(description.severity.data()));
                            resultData["vulnerability"]["classification"] =
                                FieldAlertHelper::fillEmptyOrNegative(description.classification);
                            resultData["vulnerability"]["score"]["base"] = FieldAlertHelper::fillEmptyOrNegative(
                                Utils::floatToDoubleRound(description.scoreBase, 2));
                            resultData["vulnerability"]["score"]["version"] =
                                FieldAlertHelper::fillEmptyOrNegative(description.scoreVersion);

                            resultData["vulnerability"]["type"] = "Packages";
                            resultData["vulnerability"]["updated"] = description.dateUpdated;

                            context->m_alerts[cve] = std::move(resultData);
                        });
                }
                catch (const std::exception& e)
                {
                    logError(WM_VULNSCAN_LOGTAG, "Error building event details for CVE: %s", cve.data());
                    logError(WM_VULNSCAN_LOGTAG, "Error message: %s", e.what());
                }
            }
        }
        return AbstractHandler<std::shared_ptr<TScanContext>>::handleRequest(std::move(context));
    }
};

using CVESolvedAlertDetailsBuilder = TCVESolvedAlertDetailsBuilder<>;

#endif // _CVE_SOLVED_ALERT_DETAILS_BUILDER_HPP
